package com.tao.aop;

import com.tao.annotation.AutoFill;
import com.tao.context.UserContext;
import com.tao.enumeration.OperationType;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.time.LocalDateTime;

@Aspect//标明为切面类
@Component//切面类也需要交给ioc容器管理
@Slf4j
//切面=切入点+通知
public class AutoFillAspect {
    //由于该切面方法对于insert和update，本质操作相同，但是存在细微差别，需用一定的手段进行区分
    //区分方法：由于会通过注解加细粒度，可以通过给注解设置变量，根据变量值进行区分
    //由于这种区分型的变量比较固定，为了解耦和，要不单独设置一个constant类通过字符串或0，1，2...区分，要不设置一个枚举类，效果一样
    //这里采用枚举类（为了回顾枚举）

    @Pointcut("execution(* com.tao.mapper.*.*(..) ) && @annotation(com.tao.annotation.AutoFill)" )
    public void pc(){}

    @Before("pc()")
    public void autoFill(JoinPoint joinPoint) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        log.info("开始进行公共字段填充........");
        //主要目的：根据注解的变量值给参数进行赋值



        //获取注解以及注解的变量

        //获取注解要通过连接点获取签名
        //Signature signature = joinPoint.getSignature();
        //由于默认接受类型为signature，它为一个接口，不含具体方法，因此要下转型
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //获取签名的方法，签名方法包括获取注解,由于有多个注解，需要声明为哪个注解
        AutoFill annotation = signature.getMethod().getAnnotation(AutoFill.class);//获取注解对象
        //获取注解里的变量值
        OperationType value = annotation.value();
        //获取对应方法的参数 由于参数可能有很多，获取的为数组
        Object[] args = joinPoint.getArgs();

        LocalDateTime now = LocalDateTime.now();
        Integer threadUserId = UserContext.getThreadUserId();


        if ( value.equals(OperationType.INSERT)){
            //这里规定，需要赋值的参数都放在第一个参数，这样arg[0]即第一个参数
            //赋值需要调用参数的方法，通过反射获取该方法
            Method setCreateTime = args[0].getClass().getDeclaredMethod("setCreateTime", LocalDateTime.class);
            Method setUpdateTime = args[0].getClass().getDeclaredMethod("setUpdateTime", LocalDateTime.class);
            Method setCreateUser = args[0].getClass().getDeclaredMethod("setCreateUser", Integer.class);
            Method setUpdateUser = args[0].getClass().getDeclaredMethod("setUpdateUser", Integer.class);

            //通过反射调用该方法
            setCreateTime.invoke(args[0], now);
            setUpdateTime.invoke(args[0], now);
            setCreateUser.invoke(args[0],threadUserId);
            setUpdateUser.invoke(args[0],threadUserId);
        }else {
            Method setUpdateTime = args[0].getClass().getDeclaredMethod("setUpdateTime", LocalDateTime.class);
            Method setUpdateUser = args[0].getClass().getDeclaredMethod("setUpdateUser", Integer.class);
            setUpdateTime.invoke(args[0], now);
            setUpdateUser.invoke(args[0],threadUserId);
        }

        return ;
    }
}
